package parser

import (
	"bufio"
	"errors"
	"go_redis_service/interface/IResp"
	"go_redis_service/lib/logger"
	"go_redis_service/resp/reply"
	"io"
	"runtime/debug"
	"strconv"
	"strings"
)

type PayLoad struct {
	Data IResp.Reply
	Err  error
}

type readState struct {
	//
	//  readingMultiLine
	//  @Description: 是否是多行读取模式
	//
	readingMultiLine bool
	//
	//  expectedArgCount
	//  @Description: 参数个数
	//
	expectedArgCount int
	//
	//  msgType
	//  @Description: 信息类型
	//
	msgType byte
	//
	//  args
	//  @Description: 参数
	//
	args [][]byte
	//
	//  bulkLen
	//  @Description: 字节组数据块的长度
	//
	bulkLen int64
}

func (s *readState) finished() bool {
	return s.expectedArgCount > 0 && len(s.args) == s.expectedArgCount
}

func ParseStream(reader io.Reader) <-chan *PayLoad {
	ch := make(chan *PayLoad)
	go parse0(reader, ch)
	return ch
}

func parse0(reader io.Reader, ch chan<- *PayLoad) {
	//  保证不宕掉这个函数
	defer func() {
		err := recover()
		if err != nil {
			logger.Error(string(debug.Stack()))
		}
	}()

	bufReader := bufio.NewReader(reader)
	state := &readState{}
	var err error
	var message []byte
	//
	for true {
		var ioErr bool
		//1.读取一行
		message, ioErr, err = readLine(bufReader, state)
		if err != nil {
			if ioErr {
				ch <- &PayLoad{Err: err}
				close(ch)
				return
			}
			ch <- &PayLoad{Err: err}
			// 清空状态
			state = &readState{}
		}
		// 2.是否是多行解析模式
		// 不是多行模式
		if !state.readingMultiLine {
			// 开头为*好开启解析模式
			if message[0] == '*' {
				err := parseMultiBulkHeader(message, state)
				if err != nil {
					ch <- &PayLoad{Err: errors.New("protocol error: " + string(message))}
					// 清空状态
					state = &readState{}
					continue
				}
				if state.expectedArgCount == 0 {
					ch <- &PayLoad{Data: &reply.EmptyMultiBulkReply{}}
					state = &readState{}
					continue
				}
			} else if message[0] == '$' { // $4\r\nPING\r\n
				err := parseBulkHeader(message, state)
				if err != nil {
					ch <- &PayLoad{Err: errors.New("protocol error: " + string(message))}
					// 清空状态
					state = &readState{}
					continue
				}
				if state.bulkLen == -1 {
					ch <- &PayLoad{Data: &reply.NullBulkReply{}}
					state = &readState{}
					continue
				}
			} else {
				result, err := parseSingleLineReply(message)
				ch <- &PayLoad{Data: result, Err: err}
				state = &readState{}
				continue
			}
		} else { // 3.	单行解析模式
			err = readBody(message, state)
			if err != nil {
				ch <- &PayLoad{
					Err: errors.New("protocol error: " + string(message)),
				}
				state = &readState{} // reset state
				continue
			}
			if state.finished() {
				var result IResp.Reply
				if state.msgType == '*' {
					result = reply.NewMultiBulkReply(state.args)
				} else if state.msgType == '$' {
					result = reply.NewBulkReply(state.args[0])
				}
				ch <- &PayLoad{
					Data: result,
					Err:  err,
				}
				state = &readState{}
			}
		}
	}

}

// *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
func readLine(reader *bufio.Reader, state *readState) ([]byte, bool, error) {
	var message []byte
	var err error
	// 1.待读取的字符串长度为0
	if state.bulkLen == 0 {
		message, err = reader.ReadBytes('\n')
		if err != nil {
			return nil, true, err
		}
		// 检查是否符合协议要求
		if len(message) == 0 || message[len(message)-2] != '\r' {
			return nil, false, errors.New("protocol err: " + string(message))
		}
	} else {
		// 这里在前面的单行解析头中已经获取了$3\r\n并解析处了3放在了bulkLen当中
		//SET\r\n这种类型
		message = make([]byte, state.bulkLen+2)
		_, err := io.ReadFull(reader, message)
		if err != nil {
			return nil, true, err
		}
		if len(message) == 0 || message[len(message)-1] != '\n' || message[len(message)-2] != '\r' {
			return message, true, errors.New("protocol error: " + string(message))
		}
		state.bulkLen = 0
	}
	return message, false, nil
}

// parseMultiBulkHeader
// @Description: 处理多行字符串操作的Header
// @param message
// @param state
// @return error
func parseMultiBulkHeader(message []byte, state *readState) error {
	var err error
	var expectedLine uint64
	// eg: *3\r\n => expectedLine =3
	// 截取出来 转换成int
	expectedLine, err = strconv.ParseUint(string(message[1:len(message)-2]), 10, 32)
	if err != nil {
		return errors.New("protocol error: " + string(message))
	}
	// 如果截取出来是0
	if expectedLine == 0 {
		state.expectedArgCount = 0
		return nil
	} else if expectedLine > 0 { // 后面还有行数
		state.expectedArgCount = int(expectedLine)
		state.msgType = message[0]
		state.readingMultiLine = true
		state.args = make([][]byte, 0, expectedLine)
		return nil
	} else { // 小于0 毙掉
		return errors.New("protocol error: " + string(message))
	}
}

// parseBulkHeader
// @Description: 解析当行header
// @param message
// @param state
// @return error
func parseBulkHeader(message []byte, state *readState) error {
	var err error
	// eg $4\r\n
	state.bulkLen, err = strconv.ParseInt(string(message[1:len(message)-2]), 10, 64)
	if err != nil {
		return errors.New("protocol error: " + string(message))
	}
	// $-1\r\n
	if state.bulkLen == -1 {
		return nil
	} else if state.bulkLen > 0 {
		state.msgType = message[0]
		state.readingMultiLine = true
		state.expectedArgCount = 1
		state.args = make([][]byte, 0, 1)
		return nil
	} else {
		return errors.New("protocol error: " + string(message))
	}
}

// parseSingleLineReply
// @Description:
// @param message
// @return IResp.Reply
// @return error
// eg +ok\r\n -Err\r\n
func parseSingleLineReply(message []byte) (IResp.Reply, error) {
	// 去掉\r\n

	str := strings.TrimSuffix(string(message), "\r\n")
	var result IResp.Reply
	// 判断开头的标识符
	switch message[0] {
	case '+':
		result = reply.NewStatusReply(str[1:])
	case '-':
		result = reply.NewStadardErrReply(str[1:])
	case ':':
		value, err := strconv.ParseInt(str[1:], 10, 64)
		if err != nil {
			return nil, errors.New("protocol error: " + string(message))
		}
		result = reply.NewIntReply(value)
	}
	return result, nil
}

// readBody
// @Description:
// @param message
// @param state
// @return error
// eg PING\r\n
func readBody(message []byte, state *readState) error {
	line := message[0 : len(message)-2]
	var err error
	// $3\r\n
	if line[0] == '$' {
		state.bulkLen, err = strconv.ParseInt(string(line[1:]), 10, 64)
		if err != nil {
			return errors.New("protocol error: " + string(message))
		}
		// 数组中存在空行
		if state.bulkLen <= 0 {
			state.args = append(state.args, []byte{})
			state.bulkLen = 0
		}
	} else { //SET
		state.args = append(state.args, line)
	}
	return nil
}
